package cn.faster.sys.service.impl;

import cn.faster.common.enums.ConstEnums;
import cn.faster.common.utils.sys.BASE64DecoderUtils;
import cn.faster.common.utils.sys.JsonUtils;
import cn.faster.common.utils.sys.shiro.ShiroSecurityUtils;
import cn.faster.config.security.shiro.TokenUtils;
import cn.faster.sys.entity.SysUser;
import cn.faster.sys.service.SysShiroService;
import cn.faster.sys.service.TokenService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author xiaoCoder
 * @version 1.0
 * @description: 用户登录接口
 * @date 2021/8/11 16:55
 */
@AllArgsConstructor
@Service
@Slf4j
public class SysShiroServiceImpl implements SysShiroService {

    private final StringRedisTemplate redisTemplate;

    @Override
    public Map<String, Object> login(SysUser sysUser) throws Exception {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 判断验证码信息
        log.info(">>>登录开始 ShiroServiceImpl>>>login>>>验证码验证开始>>>");
        String captcha;
        // 判断缓存里面是否有验证码（验证码是否失效）
        try {
            captcha = Objects.requireNonNull(redisTemplate.opsForValue().get("captcha")).toLowerCase(Locale.ROOT);
        } catch (Exception e) {
            log.error(">>>ShiroServiceImpl>>>验证码验证>>>验证码失效！>>>");
            map.put("status", ConstEnums.VERIFICATION_CODE_VERIFICATION.getKey());
            return map;
        }
        // 判断验证码是否为空/验证码错误
        if (sysUser.getCaptcha() == null || !sysUser.getCaptcha().equals(captcha)) {
            log.error(">>>ShiroServiceImpl>>>验证码验证>>>验证码错误！>>>");
            map.put("status", ConstEnums.VERIFICATION_CODE_ERROR.getKey());
            return map;
        }
        log.info(">>>ShiroServiceImpl>>>验证码验证>>>验证码验证成功！>>>");
        // 清空redis中的验证码
        redisTemplate.delete("captcha");
        log.info(">>>清空captcha中的验证码缓存>>>");
        // 解密前端的base64密码
        log.info("开始解密前端密码：{}", sysUser.getPassword());
        String password = BASE64DecoderUtils.decrypt(sysUser.getPassword());
        log.info("解密完成密码：{}", password);
        // 进行登录操作
        SecurityUtils.getSubject().login(new UsernamePasswordToken(sysUser.getUsername(), password, false));
        // 填充用户信息
        SysUser sysUserEntity = (SysUser) SecurityUtils.getSubject().getPrincipal();
        sysUserEntity.setDelFlag(null);
        sysUserEntity.setPassword(null);
        sysUserEntity.setSalt(null);
        // 手动实现shiro的全局存储
        ShiroSecurityUtils.setLoginSysUserInfo(sysUserEntity);
        // 加密用户信息
        String key = TokenUtils.getUserTokenKey(sysUserEntity);
        String token = TokenUtils.createUserToken(sysUserEntity);
        log.info(">>>生成token信息：{}，用户名+id:{}>>>", token, key);
        // 判断用户是否已经登录
        if (!redisTemplate.opsForValue().setIfAbsent(key, token, 10, TimeUnit.MINUTES)) {
            token = redisTemplate.opsForValue().get(key);
            // 重复登录进行续命
            redisTemplate.opsForValue().set(key, token, 10, TimeUnit.MINUTES);
            log.info(">>>重复登录>>>token续命>>>redis中有当前缓存token：{}>>>", token);
        }
        log.info(">>>登录成功>>>记录token信息：{}>>>", token);
        // 填充数据信息
        map.put("user", JsonUtils.removeEmptyFields(sysUserEntity));
        map.put("token", token);
        map.put("status", "00000");// 成功状态码！

        log.info(">>>登录完成 填充的数据信息:{}>>>", map);
        return map;
    }

    @Override
    public Map<String, Object> logout(SysUser sysUser) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        try {
            SecurityUtils.getSubject().logout();
            log.info(">>>用户退出>>>清除token>>>");
            redisTemplate.delete(TokenUtils.getUserTokenKey(sysUser));
            log.info(">>>用户退出>>>成功>>>");
            map.put("status", "00000");
            map.put("msg", "退出成功");
        } catch (Exception e) {
            log.error(">>>用户退出>>>失败>>>日志:", e);
            map.put("status", "00001");
            map.put("msg", "退出失败");
        }

        return map;
    }
}
